﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.ModelBinding;
using System.IO;
using System.ComponentModel.DataAnnotations;
using System.Web;
using System.Text;
using System.IO.Compression;
using System.Security.Principal;
using CloudStorageLight.Core;
using System.Web.OData;
using System.Web.OData.Builder;
using System.Web.OData.Query;
using System.Web.OData.Query.Validators;
using Microsoft.OData.Core;
using Microsoft.OData.Core.UriParser.Semantic;
using System.Web.OData.Routing;

namespace CloudStorageLight.Core.Web
{

    /// <summary>
    /// ODataのベースクラス
    /// </summary>
    public abstract  class BlobODataControllerBase : ODataController
    {

        public static ODataConventionModelBuilder GetModelBuilder(string name)
        {
            ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
            builder.EntitySet<FileItem>(name);
            builder.Namespace = "StorageService";
            builder.EntityType<FileItem>().Action("Checkin").ReturnsFromEntitySet<FileItem>(name);
            var checkoutAction = builder.EntityType<FileItem>().Action("Checkout");
            checkoutAction.Parameter<int>("lockTimeout");
            checkoutAction.ReturnsFromEntitySet<FileItem>(name);
            var renameAction = builder.EntityType<FileItem>().Action("Rename");
            renameAction.Parameter<string>("targetId");
            renameAction.ReturnsFromEntitySet<FileItem>(name);
            var copyAction = builder.EntityType<FileItem>().Action("Copy");
            copyAction.Parameter<string>("targetId");
            copyAction.Returns<bool>();
            var move = builder.EntityType<FileItem>().Action("Move");
            move.Parameter<string>("targetId");
            move.Returns<bool>();
            var createDirectoryAction = builder.EntityType<FileItem>().Action("CreateDirectory");
            createDirectoryAction.Parameter<string>("directoryName");
            createDirectoryAction.Parameter<bool>("ensure");
            createDirectoryAction.Returns<bool>();

            var deleteCompletely = builder.EntityType<FileItem>().Action("DeleteCompletely");

            var recover = builder.EntityType<FileItem>().Action("Recover");
            recover.Returns<bool>();

            var getAccessControl = builder.EntityType<FileItem>().Function("GetAccessControl");
            getAccessControl.Returns<string>();
            var getPasscode = builder.EntityType<FileItem>().Function("GetFilePasscode");
            getPasscode.Returns<string>();
            var getBackupMode = builder.EntityType<FileItem>().Function("GetBackupMode");
            getBackupMode.Returns<bool?>();

            var isExists = builder.EntityType<FileItem>().Function("IsExists");
            isExists.Returns<bool>();

            var getVersions = builder.EntityType<FileItem>().Function("GetVersions");
            getVersions.ReturnsCollection<string>();

            var listDeletedFiles = builder.EntityType<FileItem>().Function("ListDeletedFiles");
            listDeletedFiles.ReturnsCollection<string>();

            var listRecent = builder.EntityType<FileItem>().Collection.Function("GetRecent");
            listRecent.Parameter<int>("days");
            listRecent.ReturnsCollectionFromEntitySet<FileItem>(name);

            var getAccesslog = builder.EntityType<FileItem>().Function("GetAccessLog");
            //getAccesslog.Parameter<int>("days");
            getAccesslog.ReturnsCollection<AccessLogItem>();

            var setAccessControl = builder.EntityType<FileItem>().Action("SetAccessControl");
            setAccessControl.Parameter<string>("accessTokens");
            setAccessControl.Returns<string>();

            var setFilePasscode = builder.EntityType<FileItem>().Action("SetFilePasscode");
            setFilePasscode.Parameter<string>("passcode");
            setFilePasscode.Returns<string>();

            var setProtectedMode = builder.EntityType<FileItem>().Action("SetProtectedMode");
            setProtectedMode.Parameter<string>("mode");
            setProtectedMode.Returns<string>();

            var setUsersRole = builder.EntityType<FileItem>().Action("SetUsersRole");
            setUsersRole.Parameter<string>("usersRole");
            setUsersRole.Returns<string>();

            var setBackupMode = builder.EntityType<FileItem>().Action("SetBackupMode");
            setBackupMode.Parameter<bool?>("backup");
            setBackupMode.Returns<bool?>();

            var setCaseInsensitive = builder.EntityType<FileItem>().Action("SetCaseInsensitive");
            setCaseInsensitive.Parameter<bool?>("caseInsensitive");
            setCaseInsensitive.Returns<bool?>();


            var updateSearchContent = builder.EntityType<FileItem>().Action("UpdateSearchContent");
            updateSearchContent.Parameter<string>("title");
            updateSearchContent.Parameter<string>("searchContent");
            updateSearchContent.Returns<bool>();
            var getSearchContent = builder.EntityType<FileItem>().Function("GetSearchContent");
            getSearchContent.Returns<string>();
            var createAuthTicket = builder.EntityType<FileItem>().Action("CreateAuthTicket");
            createAuthTicket.Returns<string>();
            //var listVersion = builder.EntityType<FileItem>().Function("ListVersion");
            //listVersion.ReturnsCollection<string>();
            var backupMode = builder.EntityType<FileItem>().Function("IsBackupOn");
            backupMode.Returns<bool>();


            var startCrawl = builder.EntityType<FileItem>().Action("StartCrawl");
            startCrawl.Returns<bool>();


            return builder;

        }

        public BlobODataControllerBase()
        {
        }

        protected abstract BlobService CreateBlobService();

        protected virtual BlobUser GetCurrentUser()
        {
            return new BlobUser(System.Web.HttpContext.Current.User);
        }

        private static ODataValidationSettings _validationSettings = new ODataValidationSettings();

        protected virtual IHttpActionResult ResponseErrorMessage(Exception ex)
        {
            var blobEx = ex as BlobException;
            if (blobEx != null)
            {
                if (blobEx.Code == BlobExceptionCode.NotFoundFile)
                {
                    return NotFound();
                }
                HttpError err = new HttpError(ex.Message);
                return ResponseMessage(Request.CreateErrorResponse(HttpStatusCode.BadRequest, err));
            }
            var httpEx = ex as HttpException;
            if (httpEx != null)
            {
                return ResponseMessage(Request.CreateErrorResponse((HttpStatusCode)httpEx.GetHttpCode(), new HttpError(ex.Message)));
            }
            return ResponseMessage(Request.CreateErrorResponse(HttpStatusCode.InternalServerError, new HttpError(ex.Message)));
        }


        public class MyOrderByValidator : FilterQueryValidator
        {
            public List<string> FreeKeywords { get; set; }
            public List<string> EqDirectories { get; set; }

            public List<string> StartWithDirectories { get; set; }

            public MyOrderByValidator()
            {
                EqDirectories = new List<string>();
                StartWithDirectories = new List<string>();
                FreeKeywords = new List<string>();

            }

            private string ConvertPath(string path)
            {
                return path;
            }

            public override void ValidateSingleValueFunctionCallNode(SingleValueFunctionCallNode node, ODataValidationSettings settings)
            {
                if (node.Name == "substringof" && node.Parameters.Count() == 2)
                {
                    var col = node.Parameters.Skip(1).First() as SingleValuePropertyAccessNode;
                    var val = node.Parameters.FirstOrDefault() as ConstantNode;
                    if (val != null)
                    {
                        var literal = ConvertPath(val.LiteralText.Replace("''","'").Trim('\''));
                        if (col.Property.Name == "SearchContent" && !string.IsNullOrWhiteSpace(literal)) FreeKeywords.Add(literal);
                    }
                }
                if (node.Name == "startswith" && node.Parameters.Count() == 2)
                {
                    var col = node.Parameters.FirstOrDefault() as SingleValuePropertyAccessNode;
                    var val = node.Parameters.Skip(1).First() as ConstantNode;
                    if (val != null)
                    {
                        var literal = ConvertPath(val.LiteralText.Replace("''", "'").Trim('\''));
                        if (col.Property.Name == "Directory" && !string.IsNullOrWhiteSpace(literal)) StartWithDirectories.Add(literal);
                    }
                }

                base.ValidateSingleValueFunctionCallNode(node, settings);
            }

            public override void ValidateBinaryOperatorNode(Microsoft.OData.Core.UriParser.Semantic.BinaryOperatorNode binaryOperatorNode, ODataValidationSettings settings)
            {

                var left = binaryOperatorNode.Left as SingleValuePropertyAccessNode;
                var right = binaryOperatorNode.Right as ConstantNode;

                if (left != null && right != null)
                {
                    if (left.Property.Name == "Directory")
                    {
                        EqDirectories.Add(ConvertPath(right.LiteralText.Replace("''", "'").Trim('\'')));
                    }
                }
                base.ValidateBinaryOperatorNode(binaryOperatorNode, settings);
            }


        }

        protected virtual void LogError(string action, Exception ex)
        {
            using (var svc = CreateBlobService())
            {
                svc.EntryLog(GetCurrentUser(), action, this.Request.RequestUri.ToString(), ex.ToString());
            }
        }

        // GET: odata/Data
        public async Task<IHttpActionResult> GetData(ODataQueryOptions<FileItem> queryOptions)
        {

            queryOptions = new ODataQueryOptions<FileItem>(queryOptions.Context, this.Request);
            // validate the query.
            try
            {
                int maxsize = 0;
                var validateor = new MyOrderByValidator();
                if (queryOptions.Filter != null)
                {
                    validateor.Validate(queryOptions.Filter, new ODataValidationSettings() { });

                }
                else
                {
                    validateor.EqDirectories.Add("/");
                    queryOptions.Validate(_validationSettings);
                }

                int skip = 0;
                if (queryOptions.Skip != null)
                {
                    int.TryParse(queryOptions.Skip.RawValue, out skip);
                }
                int top = 0;
                if (queryOptions.Top != null)
                {
                     int.TryParse(queryOptions.Top.RawValue, out top);
                }

                //dire自身のPathのBlobも対象に検索するが、これは対象外にする必要があるので最大件数に＋１して検索
                if (top > 0) maxsize = top + skip + 1;
                var fulltextSerach = System.Web.HttpContext.Current.Request.QueryString["$search"];
                if (fulltextSerach != null) fulltextSerach = fulltextSerach.Trim('\'');

                using (var svc = CreateBlobService())
                {
                    var blobItems = new List<BlobItem>();

                    if (string.IsNullOrWhiteSpace(fulltextSerach) && validateor.FreeKeywords.Count == 0)
                    {
                        foreach (var dire in validateor.EqDirectories)
                        {
                            blobItems.AddRange(await svc.ListFilesAsync(GetCurrentUser(), dire, maxsize, false));

                        }
                        foreach (var dire in validateor.StartWithDirectories)
                        {
                            blobItems.AddRange(await svc.ListFilesAsync(GetCurrentUser(), dire, maxsize, true));

                        }
                    }
                    else
                    {
                        foreach (var dire in validateor.EqDirectories.Concat(validateor.StartWithDirectories))
                        {
                            if (!string.IsNullOrWhiteSpace(fulltextSerach))
                            {
                                blobItems.AddRange(await svc.SearchAsync(GetCurrentUser(), fulltextSerach, dire, maxsize));
                            }
                            foreach (var keyword in validateor.FreeKeywords)
                            {
                                blobItems.AddRange(await svc.SearchAsync(GetCurrentUser(), keyword, dire, maxsize));
                            }
                        }
                        blobItems = blobItems.Distinct().ToList();
                    }

                    var dictProtectedMode = new Dictionary<string, string>();
                    var files = new List<FileItem>();
                    foreach (BlobItem item in blobItems)
                    {
                        var file = new FileItem(item);

                        if (file.Type == "File")
                        {
                            if (!dictProtectedMode.Keys.Contains(file.Directory))
                            {
                                dictProtectedMode[file.Directory] = await svc.GetProtectedModeAsync(file.Directory);
                            }
                            var protectedMode = dictProtectedMode[file.Directory];
                            file.FileProtectedMode = protectedMode;


                        }

                        files.Add(file);
                    }
                    IQueryable results = queryOptions.ApplyTo(files.AsQueryable(), new ODataQuerySettings() { EnsureStableOrdering = false });
                    return Ok(results as IQueryable<FileItem>);

                }
            }
            catch (ODataException ex)
            {
                return BadRequest(ex.Message);
            }
            catch (Exception ex)
            {
                LogError("GetData", ex);
                return ResponseErrorMessage(ex);
            }
        }

        // GET: odata/Data(5)
        public async Task<IHttpActionResult> Get([FromODataUri] string key, ODataQueryOptions<FileItem> queryOptions)
        {
            // validate the query.
            try
            {
                queryOptions.Validate(_validationSettings);
                using (var svc = CreateBlobService())
                {
                    var path = FileItem.ConvertIdToPath(key);
                    var file = await svc.GetFileAsync(GetCurrentUser(), path);
                    var item = new FileItem(file);
                    item.FileProtectedMode = await svc.GetProtectedModeAsync(path);
                    return Ok(item);
                }
            }
            catch (ODataException ex)
            {
                return BadRequest(ex.Message);
            }
            catch(Exception ex)
            {
                LogError("Get", ex);
                return ResponseErrorMessage(ex);
            }
        }

        private bool CheckFileItem(FileItem fileItem)
        {
            if (fileItem.Directory != BlobItem.GetDirectoryPath(FileItem.ConvertIdToPath(fileItem.Id))) return false;
            return true;
        }

        // PUT: odata/Data(5)
        public async Task<IHttpActionResult> Put([FromODataUri] string key, FileItem fileItem)
        {
            if (!ModelState.IsValid || !CheckFileItem(fileItem))
            {
                return BadRequest(ModelState);
            }

            if (key != fileItem.Id)
            {
                return BadRequest();
            }

            using (var svc = CreateBlobService())
            {
                try
                {
                    var item = await svc.UpdateFileAsync(GetCurrentUser(), fileItem.Owner, FileItem.ConvertIdToPath(fileItem.Id), fileItem.Title);
                    return Updated(item);
                }
                catch (Exception ex)
                {
                    LogError("Put", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }

        // POST: odata/Data
        public async Task<IHttpActionResult> Post(FileItem fileItem)
        {
            if (!ModelState.IsValid || !CheckFileItem(fileItem))
            {
                return BadRequest(ModelState);
            }

            using(var svc = CreateBlobService())
            {
                try
                {
                    BlobItem item;
                    if (fileItem.Path.EndsWith("/"))
                    {
                        item = await svc.CreateDirectoryAsync(GetCurrentUser(), FileItem.ConvertIdToPath(fileItem.Id), fileItem.Owner);
                    }
                    else
                    {
                        item = await svc.CreateFileAsync(GetCurrentUser(), fileItem.Owner, FileItem.ConvertIdToPath(fileItem.Id), fileItem.Title);
                    }
                    return Created(item);
                }
                catch (Exception ex)
                {
                    LogError("Post", ex);
                    return ResponseErrorMessage(ex);
                }

            }
        }

        // PATCH: odata/Data(5)
        [AcceptVerbs("PATCH", "MERGE")]
        public async Task<IHttpActionResult> Patch([FromODataUri] string key, Delta<FileItem> delta)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            using (var svc = CreateBlobService())
            {
                try
                {
                    var path = FileItem.ConvertIdToPath(key);
                    var file = await svc.GetFileAsync(GetCurrentUser(), path);
                    var fileItem = new FileItem(file);
                    delta.Patch(fileItem);
                    var item = await svc.UpdateFileAsync(GetCurrentUser(), fileItem.Owner, path, fileItem.Title);
                    return Updated(item);
                }
                catch (Exception ex)
                {
                    LogError("Patch", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }

        // DELETE: odata/Data(5)
        public async Task<IHttpActionResult> Delete([FromODataUri] string key, ODataActionParameters parameters)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            using (var svc = CreateBlobService())
            {
                try
                {
                    var path = FileItem.ConvertIdToPath(key);
                    if (path.EndsWith("/"))
                    {
                        await svc.DeleteDirectoryAsync(GetCurrentUser(), path );
                    }
                    else
                    {
                        await svc.DeleteFileAsync(GetCurrentUser(), path );
                        await svc.EnqueuDeleteFullTextAsync(GetCurrentUser(), path);
                    }
                    return StatusCode(HttpStatusCode.NoContent);
                }
                catch (Exception ex)
                {
                    LogError("Delete", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }

        public async Task<IHttpActionResult> Recover([FromODataUri] string key, ODataActionParameters parameters)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }
            using (var svc = CreateBlobService())
            {
                try
                {
                    await svc.RecoverFileAsync(GetCurrentUser(), FileItem.ConvertIdToPath(key));
                    return Ok<bool>(true);
                }
                catch (Exception ex)
                {
                    LogError("Recover", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }

        //[HttpPost]
        public async Task<IHttpActionResult> Checkout([FromODataUri] string key, ODataActionParameters parameters)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }

            DateTimeOffset? lockTimeout = null;


            if (parameters != null && parameters.ContainsKey("lockTimeout"))
            {
                lockTimeout = (DateTimeOffset?)parameters["lockTimeout"];
            }


            using (var svc = CreateBlobService())
            {
                try
                {
                    var file = await svc.CheckOutAsync(GetCurrentUser(), FileItem.ConvertIdToPath(key));
                    return Ok(new FileItem(file));
                }
                catch (Exception ex)
                {
                    LogError("Checkout", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }

        //[HttpPost]
        public async Task<IHttpActionResult> Checkin([FromODataUri] string key, ODataActionParameters parameters)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }

            using (var svc = CreateBlobService())
            {
                try
                {
                    var file = await svc.CheckInAsync(GetCurrentUser(), FileItem.ConvertIdToPath(key));
                    return Ok(new FileItem(file));
                }
                catch (Exception ex)
                {
                    LogError("Checkin", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }

        public async Task<IHttpActionResult> Rename([FromODataUri] string key, ODataActionParameters parameters)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }

            var targetId = (string)parameters["targetId"];

            using (var svc = CreateBlobService())
            {
                try
                {
                    var sourceId = FileItem.ConvertIdToPath(key);
                    var file = await svc.GetFileAsync(GetCurrentUser(), sourceId);
                    await svc.RenameFileAsync(GetCurrentUser(), sourceId, targetId);
                    await svc.EnqueuRenameFullTextAsync(GetCurrentUser(), sourceId, targetId);
                    file.Path = targetId;
                    file.Title = BlobItem.GetFileName(targetId);
                    return Ok(new FileItem(file)); 
                }
                catch (Exception ex)
                {
                    LogError("Rename", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }

        public async Task<IHttpActionResult> Copy([FromODataUri] string key, ODataActionParameters parameters)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }

            var targetId = (string)parameters["targetId"];
            var temp = Path.GetTempFileName();
           
            try
            {
                using (var svc = CreateBlobService())
                {
                    try
                    {
                        if (svc.CloudAccount.StorageLimitSize.HasValue)
                        {
                            var size = WebUtil.GetUsedStoargeSize(svc.CloudAccount.AccountName);
                            if (size.HasValue && svc.CloudAccount.StorageLimitSize.Value < size.Value)
                            {
                                throw new BlobException(BlobExceptionCode.StorageQuotaExceeded);
                            }
                        }

                        var user = GetCurrentUser();
                        var sourceId = FileItem.ConvertIdToPath(key);
                        var sourceFile = await svc.GetFileAsync(user, sourceId);
                        var targetFolder = await svc.GetBlobItemAsync(user, targetId);

                        await svc.MoveOrCopyFileAsync(user, sourceFile.Path, targetFolder.Path, false);
                        await svc.EnqueuCopyFullTextAsync(GetCurrentUser(), sourceId, targetId + sourceFile.Name);
                        return Ok<bool>(true);
                    }
                    catch (Exception ex)
                    {
                        LogError("Copy", ex);
                        return ResponseErrorMessage(ex);
                    }
                }

            }
            finally
            {
                File.Delete(temp);
            }

        }

        public async Task<IHttpActionResult> Move([FromODataUri] string key, ODataActionParameters parameters)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }

            var targetId = (string)parameters["targetId"];
            var temp = Path.GetTempFileName();

            try
            {
                using (var svc = CreateBlobService())
                {
                    try
                    {
                        var user = GetCurrentUser();
                        var sourceId = FileItem.ConvertIdToPath(key);
                        var sourceFile = await svc.GetFileAsync(user, sourceId);
                        var targetFolder = await svc.GetBlobItemAsync(user, targetId);

                        await svc.MoveOrCopyFileAsync(user, sourceFile.Path, targetFolder.Path, true);
                        await svc.EnqueuCopyFullTextAsync(GetCurrentUser(), sourceId, targetId + sourceFile.Name);
                        return Ok<bool>(true);
                    }
                    catch (Exception ex)
                    {
                        LogError("Move", ex);
                        return ResponseErrorMessage(ex);
                    }
                }

            }
            finally
            {
                File.Delete(temp);
            }

        }




        public async Task<IHttpActionResult> CreateDirectory([FromODataUri] string key, ODataActionParameters parameters)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }

            var directoryName = (string)parameters["directoryName"];
            var ensure = (bool)parameters["ensure"];
            if (directoryName != null) directoryName = directoryName.Trim(' ');
            using (var svc = CreateBlobService())
            {
                try
                {
                    if (string.IsNullOrEmpty(directoryName) || !BlobUtil.IsValidPath(directoryName)) throw new BlobException(BlobExceptionCode.InvalidDirectoryPath);
                    var id = FileItem.ConvertIdToPath(key) + directoryName + "/";
                    var user = GetCurrentUser();
                    await svc.CreateDirectoryAsync(user, id, user.Name);
                    return Ok<bool>(true);
                }
                catch (BlobException bex)
                {
                    if (ensure && bex.Code == BlobExceptionCode.AlreadyExists)
                    {
                        return Ok<bool>(true);
                    }
                    LogError("CreateDirectory", bex);
                    return ResponseErrorMessage(bex);
                }
                catch (Exception ex)
                {
                    LogError("CreateDirectory", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }

        [HttpPost]
        public async Task<IHttpActionResult> DeleteCompletely([FromODataUri] string key, ODataActionParameters parameters)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            using (var svc = CreateBlobService())
            {
                try
                {
                    var path = FileItem.ConvertIdToPath(key);
                    if (path.EndsWith("/"))
                    {
                        await svc.DeleteDirectoryAsync(GetCurrentUser(), path);
                    }
                    else
                    {
                        await svc.DeleteFileAsync(GetCurrentUser(), path, true);
                        await svc.EnqueuDeleteFullTextAsync(GetCurrentUser(), path);
                    }
                    return StatusCode(HttpStatusCode.NoContent);
                }
                catch (Exception ex)
                {
                    LogError("DeleteCompletely", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }

        [HttpGet]
        public async Task<IHttpActionResult> GetAccessControl([FromODataUri] string key)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }

            using (var svc = CreateBlobService())
            {
                try
                {
                    var user = GetCurrentUser();
                    var id = FileItem.ConvertIdToPath(key);
                    if (!await svc.CanReadAsync(user, id) && !await svc.CanControlAsync(user, id)) throw new BlobException(BlobExceptionCode.NotAuthorization);
                    var item = await svc.GetBlobItemAsync(user, id);
                    return Ok(string.Join(",", item.AccessTokens.Select(x => x.Token)));
                }
                catch (Exception ex)
                {
                    LogError("GetAccessControl", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }

        [HttpGet]
        public async Task<IHttpActionResult> GetFilePasscode([FromODataUri] string key)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }

            using (var svc = CreateBlobService())
            {
                try
                {
                    var user = GetCurrentUser();
                    var id = FileItem.ConvertIdToPath(key);
                    if (!await svc.CanControlAsync(user, id)) throw new BlobException(BlobExceptionCode.NotAuthorization);
                    var item = await svc.GetBlobItemAsync(user, id);
                    return Ok(item.FilePasscode);
                }
                catch (Exception ex)
                {
                    LogError("GetFilePasscode", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }

        [HttpGet]
        public async Task<IHttpActionResult> GetBackupMode([FromODataUri] string key)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }

            using (var svc = CreateBlobService())
            {
                try
                {
                    var user = GetCurrentUser();
                    var id = FileItem.ConvertIdToPath(key);
                    if (!await svc.CanControlAsync(user, id)) throw new BlobException(BlobExceptionCode.NotAuthorization);
                    var item = await svc.GetBlobItemAsync(user, id);
                    return Ok(item.BackupMode);
                }
                catch (Exception ex)
                {
                    LogError("GetBackupMode", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }

        [HttpGet]
        public async Task<IHttpActionResult> IsExists([FromODataUri] string key)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }

            using (var svc = CreateBlobService())
            {
                try
                {
                    var id = FileItem.ConvertIdToPath(key);
                    var ret = await svc.ExistsAsync(id);
                    return Ok(ret);
                }
                catch (Exception ex)
                {
                    LogError("IsExists", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }

        [HttpGet]
        public async Task<IHttpActionResult> GetVersions([FromODataUri] string key)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }

            using (var svc = CreateBlobService())
            {
                try
                {
                    var user = GetCurrentUser();
                    var id = FileItem.ConvertIdToPath(key);
                    var list = await svc.GetVesrionsAsync(user, id);
                    var result = list.OrderByDescending(x=>x.LastModified).ThenByDescending(x => x.Snapshot.Value).Select(x => ConvertVersionString(x));
                    return Ok(result);
                }
                catch (Exception ex)
                {
                    LogError("GetVersions", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }

        private string ConvertVersionString(BlobFile blob)
        {
            var key = blob.Snapshot.Value.UtcDateTime.ToString("O");
            var txtDate = blob.LastWriteTime.HasValue ? blob.LastWriteTime.Value : blob.LastModified.Value;

            return key + "|" + txtDate.ToLocalTime().ToString("f");
        }

        [HttpGet]
        public async Task<IHttpActionResult> ListDeletedFiles([FromODataUri] string key)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }

            using (var svc = CreateBlobService())
            {
                try
                {
                    var user = GetCurrentUser();
                    var id = FileItem.ConvertIdToPath(key);
                    var list = await svc.ListFilesAsync(user, id, 0, false, true);
                    return Ok(list.Select(x=>x.Path));
                }
                catch (Exception ex)
                {
                    LogError("ListDeletedFiles", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }

        public async Task<IHttpActionResult> SetAccessControl([FromODataUri] string key, ODataActionParameters parameters)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }

            var accessTokens = (string)parameters["accessTokens"];

            using (var svc = CreateBlobService())
            {
                try
                {
                    var user = GetCurrentUser();
                    var id = FileItem.ConvertIdToPath(key);

                    var item = await svc.SetAccessControl(user, id, accessTokens);
                    return Ok(string.Join(",", item.AccessTokens.Select(x => x.Token)));
                }
                catch (Exception ex)
                {
                    LogError("SetAccessControl", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }

        public async Task<IHttpActionResult> SetFilePasscode([FromODataUri] string key, ODataActionParameters parameters)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }

            var passcode = (string)parameters["passcode"];

            using (var svc = CreateBlobService())
            {
                try
                {
                    var user = GetCurrentUser();
                    var id = FileItem.ConvertIdToPath(key);

                    var item = await svc.SetFilePasscode(user, id, passcode);
                    return Ok(item.FilePasscode);
                }
                catch (Exception ex)
                {
                    LogError("SetFilePasscode", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }

        public async Task<IHttpActionResult> SetProtectedMode([FromODataUri] string key, ODataActionParameters parameters)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }

            var mode = (string)parameters["mode"];

            using (var svc = CreateBlobService())
            {
                try
                {
                    var user = GetCurrentUser();
                    var id = FileItem.ConvertIdToPath(key);

                    var item = await svc.SetProtectedMode(user, id, mode);
                    return Ok(item.ProtectedMode);
                }
                catch (Exception ex)
                {
                    LogError("SetProtectedMode", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }

        public async Task<IHttpActionResult> SetUsersRole([FromODataUri] string key, ODataActionParameters parameters)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }

            var usersrole = (string)parameters["usersRole"];

            using (var svc = CreateBlobService())
            {
                try
                {
                    var user = GetCurrentUser();
                    var id = FileItem.ConvertIdToPath(key);
                    var item = await svc.SetUsersRole(user, id, usersrole);
                    return Ok(item.UsersRole);
                }
                catch (Exception ex)
                {
                    LogError("SetUsersRole", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }

        public async Task<IHttpActionResult> SetBackupMode([FromODataUri] string key, ODataActionParameters parameters)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }

            var backup = (bool?)parameters["backup"];

            using (var svc = CreateBlobService())
            {
                try
                {
                    var user = GetCurrentUser();
                    var id = FileItem.ConvertIdToPath(key);
                    if (!await svc.CanControlAsync(user, id)) throw new BlobException(BlobExceptionCode.NotAuthorization);

                    var item = await svc.GetBlobItemAsync(user, id);
                    item.BackupMode = backup;

                    await item.SetMetadataAsync();
                    await item.GetMetadataAsync(true);
                    return Ok(item.BackupMode);
                }
                catch (Exception ex)
                {
                    LogError("SetBackupMode", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }

        public async Task<IHttpActionResult> SetCaseInsensitive([FromODataUri] string key, ODataActionParameters parameters)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }

            var caseInsensitive = (bool?)parameters["caseInsensitive"];

            using (var svc = CreateBlobService())
            {
                try
                {
                    var user = GetCurrentUser();
                    var id = FileItem.ConvertIdToPath(key);
                    if (!await svc.CanControlAsync(user, id)) throw new BlobException(BlobExceptionCode.NotAuthorization);

                    var item = await svc.GetBlobItemAsync(user, id);
                    item.CaseInsensitive = caseInsensitive;

                    await item.SetMetadataAsync();
                    await item.GetMetadataAsync(true);
                    return Ok(item.CaseInsensitive);
                }
                catch (Exception ex)
                {
                    LogError("CaseInsensitive", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }

        public async Task<IHttpActionResult> UpdateSearchContent([FromODataUri] string key, ODataActionParameters parameters)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }

            var title = (string)parameters["title"];
            var searchContent = (string)parameters["searchContent"];

            using (var svc = CreateBlobService())
            {
                try
                {
                    var user = GetCurrentUser();
                    var id = FileItem.ConvertIdToPath(key);
                    await svc.EnqueuUpdateFullTextAsync(user, id, title, searchContent);
                    return Ok(true);
                }
                catch (Exception ex)
                {
                    LogError("UpdateSearchContent", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }

        [HttpGet]
        public async Task<IHttpActionResult> GetSearchContent([FromODataUri] string key)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }

            using (var svc = CreateBlobService())
            {
                try
                {
                    var user = GetCurrentUser();
                    var id = FileItem.ConvertIdToPath(key);
                    var text = await svc.GetFullTextAsync(user, id);
                    return Ok(text);
                }
                catch (Exception ex)
                {
                    LogError("GetSearchContent", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }


        [HttpGet]
        public async Task<IHttpActionResult> IsBackupOn([FromODataUri] string key)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }

            using (var svc = CreateBlobService())
            {
                try
                {
                    var id = FileItem.ConvertIdToPath(key);
                    var result = await svc.IsBackupOnAsync(id);
                    return Ok(result);
                }
                catch (Exception ex)
                {
                    LogError("IsBackup", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }

        [HttpGet]
        public async Task<IHttpActionResult> GetRecent([FromODataUri] int days)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }

            using (var svc = CreateBlobService())
            {
                try
                {
                    var user = GetCurrentUser();

                    var list = await svc.ListRecentAsync(user, days);
                    if (list == null) return null;


                    return Ok(list.Select(x => new FileItem(x)));

                }
                catch (Exception ex)
                {
                    LogError("GetRecent", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }

        public async Task<IHttpActionResult> StartCrawl([FromODataUri] string key)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }

            using (var svc = CreateBlobService())
            {
                try
                {
                    var user = GetCurrentUser();

                    var id = FileItem.ConvertIdToPath(key);
                    await svc.StartCrawlAsync(user, id);
                    return Ok(true);
                }
                catch (Exception ex)
                {
                    LogError("StartCrawl", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }

        [HttpGet]
        public async Task<IHttpActionResult> GetAccessLog([FromODataUri] string key)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }

            var days = 0;// MAX (int)parameters["days"];
            using (var svc = CreateBlobService())
            {
                try
                {
                    var user = GetCurrentUser();
                    var id = FileItem.ConvertIdToPath(key);
                    var list = await svc.ListFolderAccessLogAsync(user, id, days);
                    var result = list.Select(x => new AccessLogItem { AccessDate = x.Date, Path = x.Path, UserName = x.UserName, Action = x.Action });
                    return Ok(result.ToList());
                }
                catch (Exception ex)
                {
                    LogError("GetAccessLog", ex);
                    return ResponseErrorMessage(ex);
                }
            }
        }


    }
}
